package com.mazaiting.instructions.references;

import com.mazaiting.instructions.base.ClassInitLogic;
import com.mazaiting.instructions.base.Index16Instruction;
import com.mazaiting.rtda.MtFrame;
import com.mazaiting.rtda.MtOperandStack;
import com.mazaiting.rtda.MtSlots;
import com.mazaiting.rtda.heap.*;

import java.util.Objects;

/**
 * 操作静态的指令
 *
 * @author mazaiting
 * @since 2021/8/17
 */
public class StaticInstruction {

    /**
     * putstatic指令给类的某个静态变量赋值，它需要两个操作数。第一个操作数是uint16索引，来自字节码。
     * 通过这个索引可以从当 前类的运行时常量池中找到一个字段符号引用，解析这个符号引用就可以知道要给
     * 类的哪个静态变量赋值。第二个操作数是要赋给静态变量的值，从操作数栈中弹出。
     */
    public static class PutStatic extends Index16Instruction {
        /**
         * 先拿到当前方法、当前类和当前常量池，然后解析字段符号引用。
         * 如果解析后的字段是实例字段而非静态字段，则抛出IncompatibleClassChangeError异常。如果是final字段，
         * 则实际操作的是静态常量，只能在类初始化方法中给它赋值。否则，会抛出IllegalAccessError异常。
         * 类初始化方法由编译器生成，名字是 <clinit>
         *
         * @param frame 栈帧
         */
        @Override
        public void execute(MtFrame frame) {
            // 获取当前栈帧方法
            MtMethod currentMethod = frame.getMethod();
            // 获取当前类实例
            MtClass currentMtClass = currentMethod.getMtClass();
            // 获取当前类常量池
            MtConstantPool pool = currentMtClass.getConstantPool();
            // 获取字段引用
            MtConstant.MtFieldRef fieldRef = (MtConstant.MtFieldRef) pool.getConstant(index);
            // 解析字段
            MtField field = fieldRef.resolvedField();
            // 获取当前类
            MtClass mtClass = field.getMtClass();
            // 判断类是否已经初始化
            if (!mtClass.isInitStarted()) {
                frame.revertNextPc();
                ClassInitLogic.initClass(frame.getThread(), mtClass);
                return;
            }
            // 判断是否为静态字段
            if (!field.isStatic()) {
                throw new RuntimeException("java.lang.IncompatibleClassChangeError");
            }
            // 判断是否为最终字段
            if (field.isFinal()) {
                if (!Objects.equals(currentMtClass, mtClass) || !Objects.equals(currentMethod.getName(), "<clinit>")) {
                    throw new RuntimeException("java.lang.IllegalAccessError");
                }
            }
            // 获取字段描述, 槽
            String descriptor = field.getDescriptor();
            int slotId = field.getSlotId();
            // 获取类的静态变量槽
            MtSlots slots = mtClass.getStaticVars();
            // 获取操作数栈
            MtOperandStack stack = frame.getOperandStack();
            switch (descriptor.substring(0, 1)) {
                case "Z":
                case "B":
                case "C":
                case "S":
                case "I":
                    slots.setInt(slotId, stack.popInt());
                    break;
                case "F":
                    slots.setFloat(slotId, stack.popFloat());
                    break;
                case "J":
                    slots.setLong(slotId, stack.popLong());
                    break;
                case "D":
                    slots.setDouble(slotId, stack.popDouble());
                    break;
                case "L":
                case "[":
                    slots.setRef(slotId, stack.popRef());
                    break;
            }

        }

        @Override
        public String toString() {
            return "putstatic";
        }
    }

    /**
     * 它取出类的某个静态变量值，然后推入栈顶
     * getstatic指令只需要一个操作数：uint16常量池索引
     */
    public static class GetStatic extends Index16Instruction {
        /**
         * 如果解析后的字段不是静态字段，也要抛出 IncompatibleClassChangeError异常。
         * 如果声明字段的类还没有初始化好，也需要先初始化。getstatic只是读取静态变量的值，
         * 自然也就不用管它是否是final了
         * 根据字段类型，从静态变量中取出相应的值，然后推入操作数栈顶
         * @param frame 栈帧
         */
        @Override
        public void execute(MtFrame frame) {
            // 获取当前栈帧的常量池
            MtConstantPool pool = frame.getMethod().getMtClass().getConstantPool();
            // 获取字段引用
            MtConstant.MtFieldRef fieldRef = (MtConstant.MtFieldRef) pool.getConstant(index);
            // 解析字段
            MtField field = fieldRef.resolvedField();
            // 获取当前类
            MtClass mtClass = field.getMtClass();
            // 判断类是否已经初始化
            if (!mtClass.isInitStarted()) {
                frame.revertNextPc();
                ClassInitLogic.initClass(frame.getThread(), mtClass);
                return;
            }
            // 判断是否为静态字段
            if (!field.isStatic()) {
                throw new RuntimeException("java.lang.IncompatibleClassChangeError");
            }
            // 获取字段描述, 槽
            String descriptor = field.getDescriptor();
            int slotId = field.getSlotId();
            // 获取类的静态变量槽
            MtSlots slots = mtClass.getStaticVars();
            // 获取操作数栈
            MtOperandStack stack = frame.getOperandStack();
            switch (descriptor.substring(0, 1)) {
                case "Z":
                case "B":
                case "C":
                case "S":
                case "I":
                    stack.pushInt(slots.getInt(slotId));
                    break;
                case "F":
                    stack.pushFloat(slots.getFloat(slotId));
                    break;
                case "J":
                    stack.pushLong(slots.getLong(slotId));
                    break;
                case "D":
                    stack.pushDouble(slots.getDouble(slotId));
                    break;
                case "L":
                case "[":
                    stack.pushRef(slots.getRef(slotId));
                    break;
            }
        }
        @Override
        public String toString() {
            return "getstatic";
        }
    }
}


















